﻿using System;
using System.Timers;
using System.Threading;

using OptimalisationFramework.Interfaces;
using OptimalisationFramework.Utilities;

namespace OptimalisationFramework.Metaheuritics
{
    class SimulatedAnnealing : AbstractTimer,IMetaHeuristic
    {
        private ITrajectorySearch sol;
        private Random r;
        private double temperature, alfa;        
        private object locker;


        public SimulatedAnnealing(ITrajectorySearch m, double alfa, uint seconds) {           
            if (alfa <= 0 && alfa >= 1)
            {
                throw (new Exception("Wrong temperature adjust"));
            }
            this.alfa = alfa;
            sol = m;
            r = new Random();
            locker = new object();
            SetTime(seconds);
        }

        public void Start()
        {
            if (tm != null)
            {
                StartTimer();
            }
            Solve();
        }

        public void Stop() {
            stopProcess = true;
        }

        public void Solve()
        {
            ITrajectorySearch solTemp;           
            sol = sol.GenerateInitialSolution();            
            temperature = 1;
            while (!sol.StopSearch() && !stopProcess)
            {
                lock (locker)
                {
                    solTemp = sol.AdjustSolution();
                    if (solTemp.ObjectifFunction() > sol.ObjectifFunction() || boltzman(solTemp, sol))
                    {
                        sol = solTemp;
                    }
                    updateTemperature();
                }
            }
            if (tm != null)
            {
                StopTimer();
            }     
        }

        public void updateTemperature() {
            temperature = temperature * alfa;
        }

        public bool boltzman(ITrajectorySearch s_temp, ITrajectorySearch s)
        {
            if (r.Next(0, 2) < Math.Pow(Math.E, -((s_temp.ObjectifFunction() - s.ObjectifFunction()) / temperature)))
            {
                return true;
            }
            return false;
        }
    }
}
